package com.tbit.uqbike.client.business.temp_power.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.tbit.common.constant.OperationPlatform;
import com.tbit.common.entity.MachineOpLog;
import com.tbit.uqbike.client.business.machine_op_log.service.MachineOpLogService;
import com.tbit.common.entity.TempPowerCheckResult;
import com.tbit.uqbike.client.constant.*;
import com.tbit.uqbike.client.dao.core.AccountConfigExtDao;
import com.tbit.uqbike.client.exception.BaseException;
import com.tbit.uqbike.client.pojo.*;
import com.tbit.uqbike.client.pojo.newEntity.AccountConfigExt;
import com.tbit.uqbike.client.pojo.temppower.TempPowerMsg;
import com.tbit.uqbike.client.service.AccountUserService;
import com.tbit.uqbike.client.service.MQProducer;
import com.tbit.uqbike.client.service.UserService;
import com.tbit.uqbike.client.service.impl.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

/**
 * 临时通电
 *
 * @author <a href="mailto:zheng.zebiao@tbit.com.cn">zb</a>
 * @since 2024/12/12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TempPowerService {
    final MachineBorrowServiceImpl machineBorrowService;
    final MachineServiceImpl machineService;
    final RedisServiceImpl redisService;
    final AccountConfigExtDao accountConfigExtDao;
    final MachineFunctionServiceImpl machineFunctionService;
    final CalibrateServiceImpl calibrateService;
    final GeoServiceImpl geoService;
    final ParkServiceImpl parkService;
    final TerControlServiceImpl terControlService;
    final MQProducer mqProducer;
    final MachineOpLogService machineOpLogService;
    final AccountUserService accountUserService;
    final UserService userService;

    /**
     * 检查能否临时通电
     *
     * @param userId  用户id
     * @param lon     经度，无定位
     * @param lat     纬度
     * @param mapType 坐标系
     * @param isRpc   是否rpc调用，是则不鉴权
     */
    public TempPowerCheckResult canTempPower(Integer userId, Double lon, Double lat, Integer mapType, boolean isRpc) {
        // 骑行中的车才能临时通电
        MachineBorrow machineBorrow = machineBorrowService.getByUserId(userId);
        if (machineBorrow == null) {
            return TempPowerCheckResult.cannotTempPower("骑行记录不存在");
        }

        Machine machine = machineService.getByMachineId(machineBorrow.getMachineId());
        return camTempPower0(machineBorrow, machine, lon, lat, mapType, isRpc);
    }

    private TempPowerCheckResult camTempPower0(MachineBorrow machineBorrow, Machine machine, Double lon, Double lat, Integer mapType, boolean isRpc) {
        // 已临时通电次数
        Long tempPowerCountRedis = redisService.getAtomicLong(String.format("TempPower#%s#%s", machineBorrow.getOrderNO(), machine.getMachineNO()));
        int tempPowerCount = tempPowerCountRedis == null ? 0 : tempPowerCountRedis.intValue();

        //超区断电往开关 true-开  false-关
        boolean temporaryPowerSwitchOpen = Optional.ofNullable(accountConfigExtDao.selectByAccountIdAndKey(machine.getAccountId(), "temporaryPowerSwitch"))
                .map(AccountConfigExt::getParamValue)
                .map(x -> Objects.equals("1", x))
                .orElse(Boolean.FALSE);

        if (!temporaryPowerSwitchOpen && !isRpc) {
            return TempPowerCheckResult.cannotTempPower("临时通电开关未打开", tempPowerCount);
        }

        if (tempPowerCount > 0 && !isRpc) {
            log.info("[临时通电检查] 车辆: {}, 订单号: {}, 用户临时通电次数已用完", machine.getMachineNO(), machineBorrow.getOrderNO());
            return TempPowerCheckResult.cannotTempPower("临时通电次数已用完", tempPowerCount);
        }

//        兼容无定位车辆用手机位置判断是否需要弹出结果
//        boolean isNoPointCar = machineFunctionService.getValue(machine.getMachineId(), MachineStateConstant.MACHINE_TYPE_NOPOINT) == 1;

        // 24-12-06 所有车辆都需要判断是否在围栏内
        if (lon != null && lat != null) {
            Point point = calibrateService.calibrate(new Point(lon, lat), mapType, MapConstant.MAP_ORI);
            boolean inGeo = geoService.checkInGeo(machine.getAccountId(), point);
            if (inGeo && !isRpc) {
                log.info("[临时通电检查] 车辆: {}, 订单号: {}, 校验围栏内", machine.getMachineNO(), machineBorrow.getOrderNO());
                return TempPowerCheckResult.cannotTempPower("围栏内无需临时通电");
            } else {
                log.info("[临时通电检查] 车辆: {}, 订单号: {}, 超区可以通电", machine.getMachineNO(), machineBorrow.getOrderNO());
                return TempPowerCheckResult.canTempPower(tempPowerCount);
            }
        }

        log.info("[临时通电检查] 车辆: {}, 订单号: {}, 可以通电", machine.getMachineNO(), machineBorrow.getOrderNO());
        return TempPowerCheckResult.canTempPower(tempPowerCount);
    }

    /**
     * 临时通电
     *
     * @param userId      用户id
     * @param opId        操作人id
     * @param isBle       是否蓝牙解锁
     * @param orderSource 请求来源
     * @param isRpc       是否rpc调用，是则不鉴权
     */
    public void tempPower(Integer userId, Integer opId, boolean isBle, int orderSource, boolean isRpc) {
        MachineBorrow machineBorrow = machineBorrowService.getByUserId(userId);
        if (machineBorrow == null) {
            throw new BaseException("骑行记录不存在");
        }
        Machine machine = machineService.getByMachineId(machineBorrow.getMachineId());

        // 再检查一遍是否能临时通电
        TempPowerCheckResult tempPowerCheckResult = camTempPower0(machineBorrow, machine, null, null, null, isRpc);
        if (!tempPowerCheckResult.getCanTempPower()) {
            throw new BaseException(tempPowerCheckResult.getReason());
        }

        String serNO = UUID.randomUUID().toString();
        //蓝牙临时通电清除临停记录
        if (isBle) {
            parkService.delByMachineId(machine.getMachineId());
            log.info("设备临时通电清除临停记录{}", machine.getMachineNO());
        }
        terControlService.sendControl(serNO, machine.getMachineNO(), ControlTypeConstant.CONTROL_TYPE_CONTROL,
                ControlTypeConstant.CONTROL_TYPE_UNLOCK, "", machine, false, orderSource, opId,
                "用户临时通电" + (isBle ? "[BLE]" : "[GPRS]"));

        // 不再通过redis发送临时通电信息给GEO，改为通过MQ发送
//        redisService.add(String.format("BackCyclingTimeStart#%s", machine.getMachineNO()), DateUtil.now(), 600);



        // 临时上电最大时间
        int tempPowerMaxTimeSecond = Optional.ofNullable(accountConfigExtDao.selectByAccountIdAndKey(machine.getAccountId(), "temporaryPowerTime"))
                .map(AccountConfigExt::getParamValue)
                .map(Integer::parseInt)
                .filter(x -> x > 0)
                .orElse(5) * 60; // 默认值5分钟

        /*使用用户自定义上电时间*/
        String countDown = redisService.get(RedisConstant.TEMP_POWER_KEY + userId);
        if (StrUtil.isNotBlank(countDown)){
            tempPowerMaxTimeSecond = Integer.parseInt(countDown);
        }

        TempPowerMsg msg = new TempPowerMsg();
        msg.setMachineNO(machine.getMachineNO());
        msg.setOrderNO(machineBorrow.getOrderNO());
        msg.setStartTime(DateUtil.now());
        msg.setDurationSec(tempPowerMaxTimeSecond);
        mqProducer.sendToRide(RabbitMQData.queueExchange, RabbitMQData.tempPowerQueue, JSON.toJSONString(msg));

        redisService.incr(String.format("TempPower#%s#%s", machineBorrow.getOrderNO(), machine.getMachineNO()), 5 * 60 * 60);
        //清除出围栏断电标识
        redisService.del(RedisConstant.REDIS_MACHINE_OUTGEO_PUSH + machine.getMachineNO());

        // 插入车辆操作日志
        String name = null, phone = null;
        if (isRpc) {
            AccountUser user = accountUserService.getById(opId);
            if (user != null) {
                name = user.getName();
                phone = user.getPhone();
            }
        } else {
            User user = userService.getByUserId(userId);
            if (user != null) {
                name = user.getName();
                phone = user.getPhone();
            }
        }
        String detail = "骑行单号: " + machineBorrow.getOrderNO() + "\n指令流水号: " + serNO;
        machineOpLogService.insertSingle(MachineOpLog.builder()
                .accountId(machine.getAccountId())
                .machineId(machine.getMachineId())
                .machineNO(machine.getMachineNO())
                .userCode(machine.getUserCode())
                .operation("临时通电")
                .opUser(name)
                .opUserPhone(phone)
                .opPlatform(isRpc ? OperationPlatform.MANAGER.code : OperationPlatform.CLIENT.code)
                .result(true)
                .detail(detail)
                .opTime(LocalDateTime.now())
                .build()
        );
    }
}
